#include "Random.h"
#include "lang/System.h"
#include "lang/Exception.h"

using namespace yzrilyzr_lang;

namespace yzrilyzr_util{
	int64_t Random::initialScramble(int64_t seed){
		return (seed ^ multiplier) & mask;
	}
	int64_t Random::seedUniquifier(){
	  // L'Ecuyer, "Tables of Linear Congruential Generators of
	  // Different Sizes and Good Lattice Structure", 1999
		static std::atomic<int64_t> _seedUniquifier=(int64_t)8682522807148012;
		for(;;){
			int64_t current=_seedUniquifier.load();
			int64_t next=current * (int64_t)1181783497276652981;
			if(_seedUniquifier.compare_exchange_weak(current, next))
				return next;
		}
	}

	Random::Random() :Random(seedUniquifier() ^ System::nanoTime()){}
	Random::Random(int64_t seed){
		this->seed=initialScramble(seed);
	}
	double Random::nextGaussian(){
	   // See Knuth, TAOCP, Vol. 2, 3rd edition, Section 3.4.1 Algorithm C.
		if(haveNextNextGaussian){
			haveNextNextGaussian=false;
			return nextNextGaussian;
		} else{
			double v1, v2, s;
			do{
				v1=2 * nextDouble() - 1; // between -1 and 1
				v2=2 * nextDouble() - 1; // between -1 and 1
				s=v1 * v1 + v2 * v2;
			} while(s >= 1 || s == 0);
			double multiplier=std::sqrt(-2 * std::log(s) / s);
			nextNextGaussian=v2 * multiplier;
			haveNextNextGaussian=true;
			return v1 * multiplier;
		}
	}
	int32_t Random::nextInt(){
		return next(32);
	}
	//@return [0,bound)
	int32_t Random::nextInt(int32_t bound){
		if(bound <= 0)
			throw Exception("BAD_BOUND");
		int32_t r=next(31);
		int32_t m=bound - 1;
		if((bound & m) == 0)  // i.e., bound is a power of 2
			r=(int32_t)(((int64_t)bound * (int64_t)r) >> 31);
		else{ // reject over-represented candidates
			for(int32_t u=r;
				u - (r=u % bound) + m < 0;
				u=next(31))
				;
		}
		return r;
	}
	bool Random::nextBoolean(){
		return next(1) != 0;
	}
	int64_t Random::nextLong(){
		return ((int64_t)(next(32)) << 32) + next(32);
	}
	double Random::nextDouble(){
		return (((int64_t)(next(53 - 27)) << 27) + next(27)) * DOUBLE_UNIT;
	}
	float Random::nextFloat(){
		return next(24) * FLOAT_UNIT;
	}

	int32_t Random::next(int32_t bits){
		int64_t oldseed, nextseed;
		std::atomic<int64_t> & seed=this->seed;
		do{
			oldseed=seed.load();
			nextseed=(oldseed * multiplier + addend) & mask;
		} while(!seed.compare_exchange_weak(oldseed, nextseed));
		return (int32_t)(nextseed >> (48 - bits));
	}
}